﻿using System.Diagnostics;

namespace DimensionsHelper.LanguageService.Syntax;

public class MetadataFieldValueRange : SyntaxNode
{
    private SyntaxToken? _caretToken;
    private SyntaxToken? _dotDotToken;
    private Expression? _leftBoundary;
    private Expression? _rightBoundary;
    private SyntaxToken? _stepKeywordToken;
    private Expression? _stepValue;

    internal MetadataFieldValueRange(SyntaxNode? parent) :
        base(SyntaxKinds.MetadataFieldValueRange, parent)
    {
    }


    /// <summary>
    ///     区间表达式左边界
    /// </summary>
    public Expression? LeftBoundary => _leftBoundary;


    /// <summary>
    ///     区间表达式右边界
    /// </summary>
    public Expression? RightBoundary => _rightBoundary;


    /// <summary>
    ///     ^ 符号
    /// </summary>
    public SyntaxToken? CaretToken
    {
        get => _caretToken;
        internal set
        {
            Debug.Assert(value == null || value.IsNode(SyntaxKinds.CaretToken));
            SetChildNullableField(ref _caretToken, SyntaxToken.IsNullOrEmpty, value);
        }
    }


    /// <summary>
    ///     区间表达式的 '..' 符号
    /// </summary>
    public SyntaxToken? DotDotToken => _dotDotToken;


    /// <summary>
    ///     区间的 step 关键字
    /// </summary>
    public SyntaxToken? StepKeywordToken
    {
        get => _stepKeywordToken;
        internal set
        {
            Debug.Assert(value == null || value.IsNode(SyntaxKinds.StepKeywordToken));
            SetChildNullableField(ref _stepKeywordToken, value);
        }
    }


    /// <summary>
    ///     区间跳过的长度表达式
    /// </summary>
    public Expression? StepValue
    {
        get => _stepValue;
        internal set => SetChildNullableField(ref _stepValue, value);
    }


    public override bool IsEmpty => base.IsEmpty ||
                                    (LeftBoundary == null && RightBoundary == null && DotDotToken == null);


    public MetadataFieldValueRange Update(Expression leftBoundary)
    {
        Debug.Assert((DotDotToken == null && RightBoundary == null) ||
                     (DotDotToken != null && RightBoundary != null));
        SetChildNullableField(ref _leftBoundary, Expression.IsEmptyExpression, leftBoundary);
        return this;
    }


    public MetadataFieldValueRange Update(SyntaxToken dotDotToken)
    {
        Debug.Assert(dotDotToken.IsNode(SyntaxKinds.DotDotToken));
        SetChildNullableField(ref _dotDotToken, SyntaxToken.IsNullOrEmpty, dotDotToken);
        return this;
    }


    public MetadataFieldValueRange Update(SyntaxToken dotDotToken, Expression? rightBoundary)
    {
        Debug.Assert(dotDotToken.IsNode(SyntaxKinds.DotDotToken));
        SetChildNullableField(ref _dotDotToken, SyntaxToken.IsNullOrEmpty, dotDotToken);
        SetChildNullableField(ref _rightBoundary, Expression.IsEmptyExpression, rightBoundary);
        return this;
    }


    public MetadataFieldValueRange Update(Expression? leftBoundary, SyntaxToken dotDotToken)
    {
        Debug.Assert(dotDotToken.IsNode(SyntaxKinds.DotDotToken));
        SetChildNullableField(ref _leftBoundary, Expression.IsEmptyExpression, leftBoundary);
        SetChildNullableField(ref _dotDotToken, SyntaxToken.IsNullOrEmpty, dotDotToken);
        return this;
    }

    public MetadataFieldValueRange Update(SyntaxToken? caret, Expression? leftBoundary,
        SyntaxToken dotDotToken, Expression? rightBoundary)
    {
        Debug.Assert(caret == null || caret.IsNode(SyntaxKinds.CaretToken));
        Debug.Assert(dotDotToken.IsNode(SyntaxKinds.DotDotToken));
        SetChildNullableField(ref _caretToken, SyntaxToken.IsNullOrEmpty, caret);
        SetChildNullableField(ref _leftBoundary, Expression.IsEmptyExpression, leftBoundary);
        SetChildNullableField(ref _dotDotToken, SyntaxToken.IsNullOrEmpty, dotDotToken);
        SetChildNullableField(ref _rightBoundary, Expression.IsEmptyExpression, rightBoundary);
        return this;
    }


    public override void AcceptVisitor<T>(SyntaxNodeVisitor<T> visitor)
    {
        Accept(visitor, LeftBoundary, DotDotToken, RightBoundary);
    }
}


public static partial class NodeFactory
{
    public static MetadataFieldValueRange CreateMetadataFieldValueRange(SyntaxNode? parent,
        SyntaxToken? caretToken, Expression? leftBound, SyntaxToken? dotDotToken,
        Expression? rightBound)
    {
        Debug.Assert(dotDotToken == null || dotDotToken.IsNode(SyntaxKinds.DotDotToken));
        MetadataFieldValueRange range = new(parent);

        if (leftBound == null && rightBound == null && dotDotToken == null && caretToken == null)
        {
            return range;
        }

        if (dotDotToken == null)
        {
            Debug.Assert(rightBound == null && leftBound != null);
            range.CaretToken = caretToken;
            return range.Update(leftBound);
        }

        return range.Update(caretToken, leftBound, dotDotToken, rightBound);
    }
}